/*
 * Copyright (C) 2015 Daniel Uppenkamp
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.maurersmarbles.drawables;

import com.google.common.base.Preconditions;
import com.googlecode.maurersmarbles.Board;
import com.googlecode.maurersmarbles.Card;
import com.googlecode.maurersmarbles.GameLogic;
import com.googlecode.maurersmarbles.Point;
import com.googlecode.maurersmarbles.RGB;
import com.googlecode.maurersmarbles.Rectangle;
import com.googlecode.maurersmarbles.ScreenCanvas;

/**
 *
 * @author Daniel Uppenkamp
 */
public class BoardDrawable implements Drawable {

    /**
     * The scale factor used to calculate the size of the dealer chip.
     */
    protected static final float DEALER_CHIP_SCALE_FACTOR = 1.25f;
    /**
     * The scale factor used to calculate the inner size of the board.
     */
    protected static final float INNER_SIZE_SCALE_FACTOR = 0.93f;
    /**
     * The scale factor used to calculate the size of a marble on the board.
     */
    protected static final float MARBLE_SCALE_FACTOR = 0.75f;
    /**
     * The scale factor used to calculate the size of a space on the board.
     */
    protected static final float SPACE_SCALE_FACTOR = 0.6f;
    /**
     * The size of each grid cell (width and height) on the board.
     */
    protected double gridCellSize;
    /**
     * The size of the dealer chip on the board.
     */
    protected int dealerChipSize;
    /**
     * The size of a marble on the board.
     */
    protected int marbleSize;
    /**
     * The size of a space on the board.
     */
    protected int spaceSize;
    /**
     * The index for the user's marble color in the GameLogic.MARBLE_COLORS
     * array.
     */
    protected int userMarbleColorIndex = 0;
    /**
     * The rectangle for the card deck in the middle of the board.
     */
    protected Rectangle cardDeckRectangle = new Rectangle();
    /**
     * The rectangle for the discard pile in the middle of the board.
     */
    protected Rectangle discardRectangle = new Rectangle();
    /**
     * The rectangle for the playable area on the board.
     */
    protected Rectangle innerRectangle = new Rectangle();
    /**
     * The rectangle box for the board.
     */
    protected Rectangle outerRectangle = new Rectangle();

    /**
     * Creates a new BoardDrawable.
     */
    public BoardDrawable() {
        // do nothing
    }

    /**
     * Returns the grid cell at the given screen x, y coordinate.
     *
     * @param x the x location on the screen
     * @param y the y location on the screen
     * @return the grid cell at the given screen x, y coordinate
     */
    public Point getGridCellAtScreenXY(int x, int y) {

        if (innerRectangle.contains(x, y)) {
            int gridCellX = (int) ((x - innerRectangle.getX()) / gridCellSize);
            int gridCellY = (int) ((y - innerRectangle.getY()) / gridCellSize);
            return new Point(gridCellX, gridCellY);
        }
        return null;

    }

    /**
     * Returns the rectangle to draw the board in.
     *
     * @return the rectangle to draw the board in
     */
    public Rectangle getRectangle() {
        return outerRectangle;
    }

    /**
     * Sets the rectangle to draw the board in.
     *
     * @param rectangle the rectangle to draw the board in
     */
    public final void setRectangle(Rectangle rectangle) {

        Preconditions.checkNotNull(rectangle, "Null rectangle");

        this.outerRectangle = rectangle;

        // compute the inner board size and location
        int innerOffset = (int) (((1.0 - INNER_SIZE_SCALE_FACTOR) / 2.0) * outerRectangle.getWidth());
        int innerStartX = outerRectangle.getX() + innerOffset;
        int innerStartY = outerRectangle.getY() + innerOffset;
        int innerSize = (int) (outerRectangle.getWidth() - (innerOffset * 2));
        innerRectangle = new Rectangle(innerStartX, innerStartY, innerSize, innerSize);

        // save the size of each grid cell on the board
        gridCellSize = innerSize / (float) (Board.GRID_SIZE);

        // save the size of the spaces and marbles on the board
        spaceSize = (int) (gridCellSize * SPACE_SCALE_FACTOR);
        marbleSize = (int) (gridCellSize * MARBLE_SCALE_FACTOR);
        dealerChipSize = (int) (gridCellSize * DEALER_CHIP_SCALE_FACTOR);

        /**
         * Update the size and location of the card deck rectangle and discard
         * rectangle on the board. We will center the two rectangles in the
         * center of the board with a spacing of gridCellSize / 4 in between.
         */
        int cardHeight = (int) (gridCellSize * 2);
        int cardWidth = (int) (cardHeight * Card.WIDTH_TO_HEIGHT_RATIO);
        int discardStartX = (int) ((innerSize / 2.0) + innerStartX - cardWidth - (gridCellSize / 8.0));
        int discardStartY = (int) ((innerSize / 2.0) + innerStartY - (cardHeight / 2.0));
        discardRectangle = new Rectangle(discardStartX, discardStartY, cardWidth, cardHeight);
        int cardDeckStartX = (int) ((innerSize / 2.0) + innerStartX + (gridCellSize / 8.0));
        int cardDeckStartY = (int) ((innerSize / 2.0) + innerStartY - (cardHeight / 2.0));
        cardDeckRectangle = new Rectangle(cardDeckStartX, cardDeckStartY, cardWidth, cardHeight);

    }

    /**
     * Returns the rectangle for the board space with the given grid cell.
     *
     * @param gridCellPoint the grid cell for the board space
     * @return the rectangle for the board space with the given grid cell
     */
    public Rectangle getRectangleForBoardSpace(Point gridCellPoint) {
        return getRectangleForGridCell(gridCellPoint, spaceSize);
    }

    /**
     * Returns the rectangle for the card deck.
     *
     * @return the rectangle for the card deck
     */
    public Rectangle getRectangleForCardDeck() {
        return cardDeckRectangle;
    }

    /**
     * Returns the rectangle for the dealer chip for the given player number.
     *
     * @param playerNumber the player number
     * @return the rectangle for the dealer chip for the given player number
     */
    public Rectangle getRectangleForDealerChip(int playerNumber) {

        Point gridCellPoint;
        switch (playerNumber) {
            default:
            case 0:
                gridCellPoint = new Point(2, 12);
                break;
            case 1:
                gridCellPoint = new Point(2, 2);
                break;
            case 2:
                gridCellPoint = new Point(12, 2);
                break;
            case 3:
                gridCellPoint = new Point(12, 12);
                break;
        }

        return getRectangleForGridCell(gridCellPoint, dealerChipSize);

    }

    /**
     * Returns the rectangle for the discarded card.
     *
     * @return the rectangle for the discarded card
     */
    public Rectangle getRectangleForDiscardCard() {
        return discardRectangle;
    }

    /**
     * Returns the rectangle for the given grid cell using the given size.
     *
     * @param gridCellPoint the grid cell
     * @param size the size
     * @return the rectangle for the given grid cell using the given size
     */
    protected Rectangle getRectangleForGridCell(Point gridCellPoint, double size) {

        Preconditions.checkNotNull(gridCellPoint, "Null grid cell point");

        double offset = (gridCellSize - size) / 2.0;
        int x = (int) (innerRectangle.getX() + (gridCellPoint.getX() * gridCellSize) + offset);
        int y = (int) (innerRectangle.getY() + (gridCellPoint.getY() * gridCellSize) + offset);

        return new Rectangle(x, y, (int) size, (int) size);

    }

    /**
     * Returns the rectangle for a marble at the given board index.
     *
     * @param boardIndex the board index of the marble
     * @return the rectangle for a marble at the given board index
     */
    public Rectangle getRectangleForMarbleSpace(int boardIndex) {
        return getRectangleForMarbleSpace(Board.getGridCellForBoardIndex(boardIndex));
    }

    /**
     * Returns the rectangle for a marble at the given grid cell.
     *
     * @param gridCellPoint the grid cell of the marble
     * @return the rectangle for a marble at the given grid cell
     */
    public Rectangle getRectangleForMarbleSpace(Point gridCellPoint) {
        return getRectangleForGridCell(gridCellPoint, marbleSize);
    }

    /**
     * Sets the index of the user's marble color in the GameLogic.MARBLE_COLORS
     * array.
     *
     * @param userMarbleColorIndex the index of the user's marble color in the
     * GameLogic.MARBLE_COLORS array
     */
    public void setUserMarbleColorIndex(int userMarbleColorIndex) {
        this.userMarbleColorIndex = userMarbleColorIndex;
    }

    @Override
    public void draw(ScreenCanvas screenCanvas) {

        Preconditions.checkNotNull(screenCanvas, "Null screen canvas");

        // draw the board background
        screenCanvas.fillRect(outerRectangle, RGB.LIGHT_GRAY);

        // draw the empty spaces on the board
        for (int i = 0; i < Board.NUMBER_OF_BOARD_SPACES; i++) {
            screenCanvas.fillOval(getRectangleForBoardSpace(Board.getGridCellForBoardIndex(i)), RGB.DARK_GRAY);
        }

        // draw the safe spaces on the board
        for (int i = 0; i < GameLogic.NUMBER_OF_PLAYERS; i++) {
            screenCanvas.fillOval(getRectangleForBoardSpace(Board.getGridCellForBoardIndex(Board.BOARD_INDEX_SAFE[i])), GameLogic.getMarbleColorForPlayer(userMarbleColorIndex, i));
        }

        // draw the home spaces on the board
        for (int i = 0; i < GameLogic.NUMBER_OF_PLAYERS; i++) {
            for (int y = Board.BOARD_INDEX_HOME_MIN[i]; y <= Board.BOARD_INDEX_HOME_MAX[i]; y++) {
                screenCanvas.fillOval(getRectangleForBoardSpace(Board.getGridCellForBoardIndex(y)), GameLogic.getMarbleColorForPlayer(userMarbleColorIndex, i));
            }
        }

        // draw the start spaces on the board
        for (int i = 0; i < GameLogic.NUMBER_OF_PLAYERS; i++) {
            for (int y = Board.BOARD_INDEX_START_MIN[i]; y <= Board.BOARD_INDEX_START_MAX[i]; y++) {
                screenCanvas.fillOval(getRectangleForBoardSpace(Board.getGridCellForBoardIndex(y)), GameLogic.getMarbleColorForPlayer(userMarbleColorIndex, i));
            }
        }

        // draw the rectangle for the discard pile
        screenCanvas.drawRect(discardRectangle, RGB.BLACK);

        // draw the rectangle for the card deck
        screenCanvas.drawRect(cardDeckRectangle, RGB.BLACK);

    }

}
